﻿using Nop.Core;
using Nop.Core.Caching;
using Nop.Core.Data;
using Nop.Core.Domain.Security;
using Nop.Core.Domain.Stores;
using Nop.Plugin.Widgets.Carousel.Domain;
using Nop.Services.Events;
using Nop.Services.Security;
using Nop.Services.Stores;
using System.Linq;
using System;
using System.Collections.Generic;

namespace Nop.Plugin.Widgets.Carousel.Services
{
    /// <summary>
    /// Extended Customer service
    /// </summary>
    public partial class CarouselService : ICarouselService
    {

        #region Constants
        /// <summary>
        /// Key for caching
        /// </summary>
        /// <remarks>
        /// {0} : page ID
        /// </remarks>
        private const string CAROUSELS_BY_ID_KEY = "Nop.carousel.id-{0}";
        /// <summary>
        /// Key pattern to clear cache
        /// </summary>
        private const string CAROUSELS_PATTERN_KEY = "Nop.carousel.";
      
        #endregion

        #region Fields
        private readonly IRepository<CarouselObject> _carouselRepository;
        private readonly IRepository<CarouselEntity> _carouselEntityRepository;
        private readonly IEventPublisher _eventPublisher;
        private readonly ICacheManager _cacheManager;

         #endregion

        #region Ctor

        /// <summary>
        /// Ctor
        /// </summary>
        /// <param name="cacheManager">Cache manager</param>
        /// <param name="carouselRepository">Carousel repository</param>
        /// <param name="eventPublisher">Event publisher</param>
        public CarouselService(ICacheManager cacheManager,
            IRepository<CarouselObject> carouselRepository,
            IRepository<CarouselEntity> carouselEntityRepository,
            IEventPublisher eventPublisher)
        {
            this._cacheManager = cacheManager;
            this._carouselRepository = carouselRepository;
            this._carouselEntityRepository = carouselEntityRepository;
            this._eventPublisher = eventPublisher;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Delete carousel
        /// </summary>
        /// <param name="carousel">Carousel</param>
        public virtual void DeleteCarousel(CarouselObject carousel)
        {
            if (carousel == null)
                throw new ArgumentNullException("carousel");

            _carouselRepository.Delete(carousel);

            //event notification
            _eventPublisher.EntityDeleted(carousel);
        }

        /// <summary>
        /// Gets carousels widgetZones
        /// </summary>
        /// <returns>Carousel</returns>
        public virtual IList<string> GetCarouselsWidgetZones()
        {
            var widgetZones = _carouselRepository.Table.Where(x => x.Published).Select(x => x.WidgetZone).Distinct().ToArray();
            return widgetZones;
        }

        /// <summary>
        /// Gets all Carousels
        /// </summary>
        /// <param name="name">name</param>
        /// <param name="pageIndex">Page index</param>
        /// <param name="pageSize">Page size</param>
        /// <returns>Carousels</returns>
        public virtual IPagedList<CarouselObject> GetAllCarousels(string name = "", int pageIndex = 0, int pageSize = int.MaxValue)
        {
            var query = _carouselRepository.Table;
            if (!String.IsNullOrWhiteSpace(name))
                query = query.Where(s => s.Name.Contains(name));
            query = query.OrderBy(s => s.DisplayOrder);

            var carousels = query.ToList();

            //paging
            return new PagedList<CarouselObject>(carousels, pageIndex, pageSize);
        }

        /// <summary>
        /// Gets carousels by widgetZones
        /// </summary>
        /// <param name="widgetZones">widgetZones</param>
        /// <returns>Carousel</returns>
        public virtual IList<CarouselObject> GetCarouselsByWidgetZones(string widgetZones)
        {
            var query = _carouselRepository.Table.Where(c=> c.WidgetZone == widgetZones && c.Published).OrderBy(s => s.DisplayOrder);
            return query.ToList();
        }

        /// <summary>
        /// Gets a carousel
        /// </summary>
        /// <param name="carouselId">carousel identifier</param>
        /// <returns>carousel</returns>
        public virtual CarouselObject GetCarouselById(int carouselId)
        {
            if (carouselId == 0)
                return null;

            string key = string.Format(CAROUSELS_BY_ID_KEY, carouselId);
            return _cacheManager.Get(key, () => { return _carouselRepository.GetById(carouselId); });
        }

        /// <summary>
        /// Inserts a carousel
        /// </summary>
        /// <param name="carousel">Carousel</param>
        public virtual void InsertCarousel(CarouselObject carousel)
        {
            if (carousel == null)
                throw new ArgumentNullException("carousel");

            _carouselRepository.Insert(carousel);

            //event notification
            _eventPublisher.EntityInserted(carousel);
        }

        /// <summary>
        /// Updates the carousel
        /// </summary>
        /// <param name="carousel">Carousel</param>
        public virtual void UpdateCarousel(CarouselObject carousel)
        {
            if (carousel == null)
                throw new ArgumentNullException("carousel");

            _carouselRepository.Update(carousel);

            //event notification
            _eventPublisher.EntityUpdated(carousel);
        }

        #region Carousel Entity

        /// <summary>
        /// Deletes a carousel Entity
        /// </summary>
        /// <param name="carouselEntity">Carousel Entity</param>
        public virtual void DeleteCarouselEntity(CarouselEntity carouselEntity)
        {
            if (carouselEntity == null)
                throw new ArgumentNullException("carouselEntity");

            _carouselEntityRepository.Delete(carouselEntity);

            //event notification
            _eventPublisher.EntityDeleted(carouselEntity);
        }

        /// <summary>
        /// Gets a carousel Entities by carousel identifier
        /// </summary>
        /// <param name="carouselId">The carousel identifier</param>
        /// <returns>Carousel Entities</returns>
        public virtual IList<CarouselEntity> GetCarouselEntitiesByCarouselId(int carouselId)
        {
            var query = from pp in _carouselEntityRepository.Table
                        where pp.CarouselId == carouselId
                        orderby pp.DisplayOrder
                        select pp;
            var carouselPictures = query.ToList();
            return carouselPictures;
        }

        /// <summary>
        /// Gets a carousel Entity
        /// </summary>
        /// <param name="carouselEntityId">Carousel Entity identifier</param>
        /// <returns>Carousel Entity</returns>
        public virtual CarouselEntity GetCarouselEntityById(int carouselEntityId)
        {
            if (carouselEntityId == 0)
                return null;

            return _carouselEntityRepository.GetById(carouselEntityId);
        }

        /// <summary>
        /// Inserts a carousel Entity
        /// </summary>
        /// <param name="carouselEntity">Carousel Entity</param>
        public virtual void InsertCarouselEntity(CarouselEntity carouselEntity)
        {
            if (carouselEntity == null)
                throw new ArgumentNullException("carouselEntity");

            _carouselEntityRepository.Insert(carouselEntity);

            //event notification
            _eventPublisher.EntityInserted(carouselEntity);
        }

        /// <summary>
        /// Updates a carousel Entity
        /// </summary>
        /// <param name="carouselEntity">Carousel Entity</param>
        public virtual void UpdateCarouselEntity(CarouselEntity carouselEntity)
        {
            if (carouselEntity == null)
                throw new ArgumentNullException("carouselEntity");

            _carouselEntityRepository.Update(carouselEntity);

            //event notification
            _eventPublisher.EntityUpdated(carouselEntity);
        }

        #endregion

        #endregion


        /// <summary>
        /// Save entity
        /// </summary>
        /// <typeparam name="T">Type</typeparam>
        /// <param name="entity">Entity</param>
        public virtual void SaveCarouselEntity<T>(T entity, int carouselId) where T : BaseEntity
        {
            if (entity == null)
                throw new ArgumentNullException("entity");

            int entityId = entity.Id;
            string entityName = typeof(T).Name;
            

            var query = from ce in _carouselEntityRepository.Table
                        where ce.EntityId == entityId &&
                        ce.EntityName == entityName &&
                        ce.CarouselId == carouselId
                        orderby ce.Id descending
                        select ce;
            var allCarouselEntities = query.ToList();
            var existingCarouselEntity = allCarouselEntities.FirstOrDefault();

            if (existingCarouselEntity == null)
            {
                //new record
                var carouselEntity = new CarouselEntity()
                {
                    CarouselId = carouselId,
                    EntityId = entity.Id,
                    EntityName = entityName,
                    Published = true,
                };
                InsertCarouselEntity(carouselEntity);
            }
            else 
            {
                existingCarouselEntity.Published = true;
                UpdateCarouselEntity(existingCarouselEntity);
            }
        }
    }
}
